<?php
	
	define('In','2');
	define('Out','1');
	define('Neuron', '10');
	$Data = file_rows_num('deal_data/train_data.txt');//样本数据个数

	$d_in = array();	//存储Data个样本的In个输入
	$d_out = array();	//存储Data个样本的Out个输出
	$o = array();		//神经元通过映射函数对外的输出
	$w = array();		//隐藏层神经元权值
	$v = array();		//输出层神经元权值
	$dw = array();		//与之对应的保存修正量
	$dv = array(); 		//与之对应的保存修正量
	
	$Maxin = array();	//保存每个输入最大最小值，用可以用于归一化
	$Minin = array();	//同上
	$Maxout = array();	//保存最大输出
	$Minout = array();	//同上

	$OutputData = array();	//输出的数值
	$ESP = 0.01;			//误差精度
	$i = 0;$j = 0;
	$A = 0.2;$B = 0.4;$a = 0.3;$b = 0.3;
	$TrainC=200;			//训练次数

	function file_rows_num($file)
	{
		$fp=fopen($file, "r");
		$i=0;
		while(!feof($fp)) {
		//每次读取2M
			if($data=fread($fp,1024*1024*2)){
			//计算读取到的行数
				$num=substr_count($data,"\n");
				$i+=$num;
			}
		}
		fclose($fp);
		return $i;
	}
	function ReadSample($file)
	{
		$fp = fopen($file,'r');
		$i=0;
		global $Data;
		global $d_in,$d_out;
		while(!feof($fp)&&$i<$Data)
		{
			$line = fgets($fp);
			$arr = explode(' ', $line);
			// var_dump($arr);
			foreach ($arr as $key => $value) 
			{
				if($key<In){
					$d_in[$i][$key] = $value;
				}else{
					$d_out[$i][$key-In] = $value;
				}
			}
			$i++;
		}
	}
	function InitBPNetwork()
	{
		global $Data;
		global $d_in,$d_out;
		global $Minin,$Maxin;
		global $Minout,$Maxout;
		global $w,$v;
		global $dw,$dv;
		//找到数据最大值 最小值
		for($i=0;$i<In;$i++){//每个特征值都要归一化
			$Minin[$i] = $Maxin[$i] = $d_in[0][$i];
			for($j=0;$j<$Data;$j++){//找出数据中最大值和最小以进行归一化
				$Maxin[$i] = $Maxin[$i]>$d_in[$j][$i]?$Maxin[$i]:$d_in[$j][$i];
				$Minin[$i] = $Minin[$i]<$d_in[$j][$i]?$Minin[$i]:$d_in[$j][$i];
			}
		}
		for($i=0;$i<Out;$i++){
			$Minout[$i] = $Maxout[$i] = $d_out[0][$i];
			for($j=0;$j<$Data;$j++){
				$Maxout[$i] = $Maxout[$i]>$d_out[$j][$i]?$Maxout[$i]:$d_out[$j][$i];
				$Minout[$i] = $Minout[$i]<$d_out[$j][$i]?$Minout[$i]:$d_out[$j][$i];
			}
		}
		//归一化处理
		for($i=0;$i<In;$i++){
			for($j=0;$j<$Data;$j++){
				$d_in[$j][$i] = ($d_in[$j][$i]-$Minin[$i]+1)/($Maxin[$i]-$Minin[$i]+1);
			}
		}
		for($i=0;$i<Out;$i++){
			for($j=0;$j<$Data;$j++){
				$d_out[$j][$i] = ($d_out[$j][$i]-$Minout[$i]+1)/($Maxout[$i]-$Minout[$i]+1);
			}
		}
		//一层
		for($i=0;$i<Neuron;$i++){//55个神经元，可通过neuron改变
			for($j=0;$j<In;$j++){
				$w[$i][$j]=GetRand();//产生一个 -1~1 之间的数据
				$dw[$i][$j]=0; //修正量 初始设置为0
			}
		}
		//第二层
		for($i=0;$i<Neuron;$i++){
			for($j=0;$j<Out;$j++){
				$v[$j][$i]=GetRand();
				$dv[$j][$i]=0;
			}
		}
	}
	function ComputO($var)
	{
		$sum = 0.0;
		global $d_in,$o,$OutputData;
		global $w,$v;
		//神经元输出公式  x_y=Sigma(y_i*w_ji) 
		for($i=0;$i<Neuron ;$i++){
			$sum=0;
			for($j=0;$j<In;$j++){
				$sum += $w[$i][$j]*$d_in[$var][$j];
			}
			//隐藏层到输出层输出 公式 y_i=(1/(1+e^(-x_j)))  Sigmoid函数  (自然增长率)
			$o[$i] = 1/(1+exp(-1*$sum));
		}
		
		for($i=0;$i<Out;$i++){
			$sum=0;
			for($j=0;$j<Neuron;$j++){
				$sum+=$v[$i][$j]*$o[$j];
			}
			$OutputData[$i]=$sum; //OutputData BP神经网络预测样本结果
		}
	}
	function BackUpdate($var)
	{
		global $A,$B,$a,$b;
		global $OutputData,$d_out,$d_in,$o;
		global $dw,$dv,$w,$v;
		for($i=0;$i<Neuron;$i++){
			$t = 0.0;
			for($j=0;$j<Out;$j++){
				$t+=($OutputData[$j]-$d_out[$var][$j])*$v[$j][$i];
				$dv[$j][$i]=$A*$dv[$j][$i]+$B*($OutputData[$j]-$d_out[$var][$j])*$o[$i];//2 4
				$v[$j][$i] -= $dv[$j][$i];
			}
			for($j=0;$j<In;$j++){
				$dw[$i][$j]=$a*$dw[$i][$j]+$b*$t*$o[$i]*(1-$o[$i])*$d_in[$var][$j];//2 3
				$w[$i][$j] -= $dw[$i][$j];
			}
		}
	}
	function TrainNetwork()
	{
		global $Data;
		global $OutputData,$d_out;
		global $TrainC,$ESP;
		$c=0;
		do{
			$e=0;
			for($i=0;$i<$Data;$i++){
				ComputO($i); //BP神经网络产生预测的结果
				for($j=0;$j<Out;$j++){
					$e+=abs(($OutputData[$j]-$d_out[$i][$j])/$d_out[$i][$j]);
				}
				BackUpdate($i); //通过预测的结果和样本的准确结果进行比对，然后对神经网络进行误差量的修正
			}
			if($c%100==0){
				echo 'TrainTime:'.$c.'TrainESP'.$e/$Data.'<br>';
			}
			$c++;			
		}while($c<$TrainC && $e/$Data>$ESP);
		//cout<<e/Data<<endl;
		//对于平均误差e进行监控，如果达到了设定的精度即完成训练，如果不能达到预期的精度要求，那就在指定的训练次数内进行	
	}
	function Result($var)
	{
		global $w,$v;
		global $Minin,$Maxin;
		global $Minout,$Maxout;	
		// var_dump($w);
		$result = array();
		$sum=0.0;
		$temp = array();
		for($i=0;$i<In;$i++){
			$temp[$i]=($var[$i]-$Minin[$i]+1)/($Maxin[$i]-$Minin[$i]+1);
		}
		//神经元输出公式  x_y=Sigma(y_i*w_ji) 
		for($i=0;$i<Neuron ;$i++){
			$sum=0.0;
			for($j=0;$j<In;$j++){
				$sum += $w[$i][$j]*$temp[$j];
			}
			
			//隐藏层到输出层输出 公式 y_i=(1/(1+e^(-x_j)))  Sigmoid函数  (自然增长率)
			$o[$i] = 1/(1+exp(-1*$sum));
		}
		for($i=0;$i<Out;$i++){
			$sum=0.0;
			for($j=0;$j<Neuron;$j++){
				$sum+=$v[$i][$j]*$o[$j];
			}
			$OutputData[$i]=$sum; //OutputData BP神经网络预测样本结果
		}
		for($i=0;$i<Out;$i++){
			$result[$i]=$sum*($Maxout[$i]-$Minout[$i]+1)+$Minout[$i]-1;
		}
		return $result;
	}
	// 获取-1到1的随机数
	function GetRand(){
		return  mt_rand(1,30000)/15000-1;
	}
	function ReadValue()
	{
		global $w,$v;
		global $Minin,$Maxin;
		global $Minout,$Maxout;
		$file = 'deal_data/value.txt';
		$data = file_get_contents($file);
		$data = str_replace(PHP_EOL, '', $data);
		$arr = explode(' ', $data);
		// var_dump($arr);
		$k = 0;
		for($i=0;$i<Neuron;$i++)
		{
			for($j=0;$j<In;$j++)
			{
				$w[$i][$j] = $arr[$k];
				$k++;
			}
		}
		for($i=0;$i<Out;$i++)
		{
			for($j=0;$j<Neuron;$j++)
			{
				$v[$i][$j] = $arr[$k];
				$k++;
			}
		}
		//读取归一化的最大最小值
		for($i=0;$i<In;$i++)
		{
			$Minin[$i] = $arr[$k];
			$k++;
		}
		for($i=0;$i<In;$i++)
		{
			$Maxin[$i] = $arr[$k];
			$k++;
		}
		for($i=0;$i<Out;$i++)
		{
			$Minout[$i] = $arr[$k];
			$k++;
		}
		for($i=0;$i<Out;$i++)
		{
			$Maxout[$i] = $arr[$k];
			$k++;
		}
	}

?>